package org.tech4j.common.zip.gzip;

import org.apache.commons.lang3.ArrayUtils;
import org.tech4j.common.spi.Join;
import org.tech4j.common.zip.api.CompressData;
import org.tech4j.common.zip.api.CompressException;
import org.tech4j.common.zip.api.DecompressException;
import org.tech4j.common.zip.api.DefaultCompressData;
import org.tech4j.common.zip.api.ZipExecutor;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Objects;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

/**
 * @program: tech4j-common
 * @description:
 * @author: YuKai Fan
 * @create: 2025/3/3 20:54
 **/
@Join
public class GzipExecutor implements ZipExecutor {

    private static int DEFAULT_BUFFER_SIZE = 8192;

    @Override
    public int maxCompressedLength(int maxLength) {
        DEFAULT_BUFFER_SIZE = maxLength;
        return maxLength;
    }

    @Override
    public CompressData compress(byte[] data) {
        if (ArrayUtils.isEmpty(data)) {
            throw new CompressException("Gzip Prepare compressed data is empty");
        }
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try (GZIPOutputStream gzip = new GZIPOutputStream(out)) {
            gzip.write(data);
            gzip.flush();
            gzip.finish();
            return new DefaultCompressData(data.length, out.toByteArray());
        } catch (IOException e) {
            throw new CompressException("Gzip compress error", e);
        }
    }

    @Override
    public void compress(InputStream input, OutputStream output) {
        try (GZIPOutputStream gzip = new GZIPOutputStream(output)) {
            byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
            int n;
            while ((n = input.read(buffer, 0, buffer.length)) > -1) {
                gzip.write(buffer, 0, n);
            }
            gzip.finish();
            gzip.finish();
        } catch (IOException e) {
            throw new CompressException("Gzip compress error", e);
        }
    }

    @Override
    public byte[] decompress(CompressData data) {
        if (Objects.isNull(data) || ArrayUtils.isEmpty(data.getCompressedData())) {
            throw new CompressException("Gzip compressed data is empty");
        }
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try (GZIPInputStream gzip = new GZIPInputStream(new ByteArrayInputStream(data.getCompressedData()))) {
            byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
            int n;
            while ((n = gzip.read(buffer)) > -1) {
                out.write(buffer, 0, n);
            }
            return out.toByteArray();
        } catch (IOException e) {
            throw new DecompressException("Gzip decompress error", e);
        }
    }

    @Override
    public void decompress(InputStream input, OutputStream output) {
        try (GZIPInputStream gzip = new GZIPInputStream(input)) {
            byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
            int n;
            while ((n = gzip.read(buffer, 0, buffer.length)) > -1) {
                output.write(buffer, 0, n);
            }
        } catch (IOException e) {
            throw new DecompressException("Gzip decompress error", e);
        }
    }
}
